import { Filter, ObjectId } from 'mongodb'
import { sendLogMessageTo } from '../listen'
import db from '../db'
import { BaseInfo, ChatRoom, FetchAPI, FetchResult, LogAll, LoginUser } from '../interfaces'
import { ERRORS, getBaseInfo } from '../utils/common'

/**
 *
 * @param chatroom
 * @param loginUser
 * @param allow 添加用户允许非群主操作
 * @returns
 */
const hasErrors = (chatroom: ChatRoom & BaseInfo, loginUser: LoginUser, allow?: boolean) => {
  if (!chatroom) {
    return {
      error: ERRORS.CHATROOM_404
    }
  }
  if (allow) {
    return
  }
  /** 有无权限 */
  for (let i = 0; i < chatroom.users.length; i++) {
    const user = chatroom.users[i];
    if (user.username === loginUser.username) {
      if (user.is_admin || user.is_master) {
        return
      }
      return {
        error: ERRORS.PERMISSION_DENIED
      }
    }
  }
}

/** 获取当前用户所有群聊 */
export const chatroom_list: FetchAPI<Filter<ChatRoom & BaseInfo>, Array<ChatRoom & BaseInfo>> = async (
  query = {},
  { loginUser },
) => {
  const conn = await db.getCollection('chatroom')
  const list = await conn.find({
    ...query,
    $or: [
      { "users.username": loginUser.username, },
      { delete_users: loginUser.username, },
    ],
  }).toArray()
  return {
    success: true,
    data: list
  }
}

/** 获取群聊最新信息 */
export const chatroom_info: FetchAPI<{ _id: string }, ChatRoom & BaseInfo> = async ({ _id }, { loginUser }) => {
  const conn = await db.getCollection('chatroom')
  const chatroom = await conn.findOne({ _id: new ObjectId(_id), "users.username": loginUser.username, })
  const errors = hasErrors(chatroom, loginUser, true)
  if (errors) {
    return errors
  }
  return {
    success: true,
    data: chatroom,
  }
}

/** 新建群聊 */
export const chatroom_create: FetchAPI<ChatRoom, ChatRoom & BaseInfo> = async (
  chatroom,
  { loginUser }
) => {
  const baseInfo = getBaseInfo(loginUser)
  const conn = await db.getCollection('chatroom')
  const to = chatroom.users.map(u => {
    u.is_master = u.username === loginUser.username
    u.is_admin = u.is_master || u.is_admin
    return u.username
  })
  const info: ChatRoom & BaseInfo = {
    ...chatroom,
    ...baseInfo,
  }
  const { insertedId } = await conn.insertOne(info)
  info._id = insertedId
  const conn_log = await db.getCollection('log')
  const { insertedId: logId } = await conn_log.insertOne({ type: 'chatroom_create', chatroom_id: info._id, message: info, to, ...baseInfo })
  sendLogMessageTo(to, 'chatroom_create', info, { _id: logId, ...baseInfo }, info._id)
  return {
    success: true,
    data: info,
  }
}

/** 修改群聊基本信息 */
export const chatroom_update: FetchAPI<
  Pick<ChatRoom & BaseInfo, '_id' | 'name' | 'desc' | 'notice'>,
  ChatRoom & BaseInfo
> = async (
  { _id, name, desc, notice },
  { loginUser }
) => {
    const conn = await db.getCollection('chatroom')
    const chatroom = await conn.findOne({ _id: new ObjectId(_id), "users.username": loginUser.username, })
    const errors = hasErrors(chatroom, loginUser)
    if (errors) {
      return errors
    }
    const baseInfo = getBaseInfo(loginUser, chatroom)
    const info = { ...chatroom, ...baseInfo, name, desc, notice }
    await conn.updateOne({ _id: new ObjectId(_id) }, {
      $set: {
        ...baseInfo,
        name, desc, notice
      }
    })
    const to = chatroom.users.map(u => u.username)
    const conn_log = await db.getCollection('log')
    const logBaseInfo = getBaseInfo(loginUser)
    const { insertedId: logId } = await conn_log.insertOne({ type: 'chatroom_update', chatroom_id: info._id, message: info, to, ...logBaseInfo })
    sendLogMessageTo(to, 'chatroom_update', info, { _id: logId, ...logBaseInfo }, info._id)
    return {
      success: true,
      data: info,
    }
  }

/** 添加成员 */
export const chatroom_adduser: FetchAPI<
  Pick<ChatRoom & BaseInfo, '_id' | 'users'>,
  ChatRoom & BaseInfo
> = async (
  { _id, users },
  { loginUser }
) => {
    const conn = await db.getCollection('chatroom')
    const chatroom = await conn.findOne({ _id: new ObjectId(_id), "users.username": loginUser.username, })
    const errors = hasErrors(chatroom, loginUser, true)
    if (errors) {
      return errors
    }
    const baseInfo = getBaseInfo(loginUser, chatroom)
    const add_users = users.filter(u => !chatroom.users.find(u1 => u1.username === u.username))
    const _users = chatroom.users.concat(add_users)
    const info = { ...chatroom, ...baseInfo, users: _users }
    await conn.updateOne({ _id: new ObjectId(_id) }, {
      $set: {
        ...baseInfo,
        users: _users,
        delete_users: chatroom.delete_users?.filter(u => !users.find(_u => _u.username === u))
      }
    })
    const to = _users.map(u => u.username)
    const conn_log = await db.getCollection('log')
    const logBaseInfo = getBaseInfo(loginUser)
    const { insertedId: logId } = await conn_log.insertOne({
      type: 'chatroom_adduser', chatroom_id: info._id, message: {
        chatroom_id: chatroom._id,
        users: add_users.map(u => u.username)
      }, to, ...logBaseInfo
    })
    sendLogMessageTo(to, 'chatroom_adduser', {
      chatroom_id: chatroom._id,
      users: add_users.map(u => u.username)
    }, { _id: logId, ...logBaseInfo }, info._id)
    return {
      success: true,
      data: info,
    }
  }

/** 删除成员 */
export const chatroom_deluser: FetchAPI<
  { _id: BaseInfo['_id'], users: string[] },
  ChatRoom & BaseInfo
> = async (
  { _id, users = [] },
  { loginUser }
) => {
    const conn = await db.getCollection('chatroom')
    const chatroom = await conn.findOne({ _id: new ObjectId(_id), "users.username": loginUser.username, })
    // 允许删除自己
    const is_del_myself = users.length === 1 && loginUser.username === users[0]
    const errors = hasErrors(chatroom, loginUser, is_del_myself)
    if (errors) {
      return errors
    }
    const baseInfo = getBaseInfo(loginUser, chatroom)
    const _users = chatroom.users.filter(u => !users.includes(u.username))
    const info = { ...chatroom, ...baseInfo, users: _users }
    await conn.updateOne({ _id: new ObjectId(_id) }, {
      $set: {
        ...baseInfo,
        users: _users,
        delete_users: users,
      }
    })
    const to = chatroom.users.map(u => u.username)
    const conn_log = await db.getCollection('log')
    const logBaseInfo = getBaseInfo(loginUser)
    const { insertedId: logId } = await conn_log.insertOne({
      type: 'chatroom_deluser', chatroom_id: info._id, message: {
        chatroom_id: chatroom._id,
        users,
      }, to, ...logBaseInfo
    })
    sendLogMessageTo(to, 'chatroom_deluser', {
      chatroom_id: chatroom._id,
      users,
    }, { _id: logId, ...logBaseInfo }, info._id)
    return {
      success: true,
      data: info,
    }
  }

/** 修改成员 = 先删除 + 后新增 */
export const chatroom_updateuser: FetchAPI<
  { _id: BaseInfo['_id'], users: string[] },
  ChatRoom & BaseInfo
> = async (
  { _id, users = [] },
  { loginUser }
) => {
    const conn = await db.getCollection('chatroom')
    const chatroom = await conn.findOne({ _id: new ObjectId(_id), "users.username": loginUser.username, })
    const errors = hasErrors(chatroom, loginUser, true)
    if (errors) {
      return errors
    }
    const add_users = new Set(users)
    const del_users: string[] = []
    chatroom.users.forEach(u => {
      if (add_users.has(u.username)) {
        add_users.delete(u.username)
      } else {
        del_users.push(u.username)
      }
    })
    let res: FetchResult<ChatRoom & BaseInfo>
    if (del_users.length > 0) {
      res = await chatroom_deluser({ _id, users: del_users }, { loginUser })
      if (res.error) {
        return res
      }
    }
    if (add_users.size > 0) {
      res = await chatroom_adduser({ _id, users: [...add_users].map(username => ({ username })) }, { loginUser })
    }
    return res
  }

/** 成员属性修改 */
export const chatroom_user_change: FetchAPI<
  LogAll['chatroom_user_change'],
  ChatRoom & BaseInfo
> = async (
  message,
  { loginUser }
) => {
    const { chatroom_id: _id, user, action } = message
    const conn = await db.getCollection('chatroom')
    const chatroom = await conn.findOne({ _id: new ObjectId(_id), "users.username": loginUser.username, })
    const errors = hasErrors(chatroom, loginUser)
    if (errors) {
      return errors
    }
    const baseInfo = getBaseInfo(loginUser, chatroom)
    const users = chatroom.users.map(u => {
      if (action === 'to_master') {
        // 转让群
        return { ...u, is_master: u.username === user }
      }
      if (u.username === user) {
        switch (action) {
          case 'to_admin': u.is_admin = true; break;
          case 'off_admin': u.is_admin = false; break;
          case 'to_hidden': u.is_hidden = true; break;
          case 'off_hidden': u.is_hidden = false; break;
          case 'to_mute': u.is_muted = true; break;
          case 'off_mute': u.is_muted = false; break;
        }
        return { ...u }
      }
      return u
    })
    const info = { ...chatroom, ...baseInfo, users }
    await conn.updateOne({ _id: new ObjectId(_id) }, {
      $set: {
        ...baseInfo,
        users,
      }
    })
    const conn_log = await db.getCollection('log')
    const logBaseInfo = getBaseInfo(loginUser)
    const to = chatroom.users.map(u => u.username)
    const { insertedId: logId } = await conn_log.insertOne({ type: 'chatroom_user_change', chatroom_id: info._id, message, to, ...logBaseInfo })
    sendLogMessageTo(to, 'chatroom_user_change', message, { _id: logId, ...logBaseInfo }, info._id)
    return {
      success: true,
      data: info,
    }
  }

/** 全体禁言 */
export const chatroom_mute: FetchAPI<
  LogAll['chatroom_mute'],
  ChatRoom & BaseInfo
> = async (
  message,
  { loginUser }
) => {
  const { chatroom_id: _id, action } = message
    const conn = await db.getCollection('chatroom')
    const chatroom = await conn.findOne({ _id: new ObjectId(_id), "users.username": loginUser.username, })
    const errors = hasErrors(chatroom, loginUser)
    if (errors) {
      return errors
    }
    const baseInfo = getBaseInfo(loginUser, chatroom)
    const info = { ...chatroom, ...baseInfo }
    switch (action) {
      case 'to_mute': info.is_muted = true; break;
      case 'off_mute': info.is_muted = false; break;
    }
    await conn.updateOne({ _id: new ObjectId(_id) }, {
      $set: {
        ...baseInfo,
        is_muted: info.is_muted,
      }
    })
    const to = chatroom.users.map(u => u.username)
    const conn_log = await db.getCollection('log')
    const logBaseInfo = getBaseInfo(loginUser)
    const { insertedId: logId } = await conn_log.insertOne({ type: 'chatroom_mute', chatroom_id: info._id, message, to, ...logBaseInfo })
    sendLogMessageTo(to, 'chatroom_mute', message, { _id: logId, ...logBaseInfo }, info._id)
    return {
      success: true,
      data: info,
    }
  }

/** 解散群聊 */
export const chatroom_delete: FetchAPI<
  Pick<ChatRoom & BaseInfo, '_id' | 'delete_reason'>,
  ChatRoom & BaseInfo
> = async (
  { _id, delete_reason },
  { loginUser }
) => {
    const conn = await db.getCollection('chatroom')
    const chatroom = await conn.findOne({ _id: new ObjectId(_id), "users.username": loginUser.username, })
    const errors = hasErrors(chatroom, loginUser)
    if (errors) {
      return errors
    }
    const baseInfo = getBaseInfo(loginUser, chatroom)
    chatroom.is_deleted = true
    const info = { ...chatroom, is_deleted: true, delete_reason, ...baseInfo }
    await conn.updateOne({ _id: new ObjectId(_id) }, {
      $set: {
        is_deleted: true,
        delete_reason,
        ...baseInfo,
      }
    })
    const to = chatroom.users.map(u => u.username)
    const conn_log = await db.getCollection('log')
    const logBaseInfo = getBaseInfo(loginUser)
    const { insertedId: logId } = await conn_log.insertOne({ type: 'chatroom_delete', chatroom_id: info._id, message: info, to, ...logBaseInfo })
    sendLogMessageTo(to, 'chatroom_delete', info, { _id: logId, ...logBaseInfo }, info._id)
    return {
      success: true,
      data: info,
    }
  }
